sequenceDiagram
autonumber
participant T1 as Виртуальный Поток 1
participant T2 as Виртуальный Поток 2
participant Map as ConcurrentHashMap
participant B as Bucket Ячейка Памяти
Note over T1, T2: Одновременный старт такт сессии для одного пользователя (Cache Miss)
%% ПОТОК 1 СТАРТУЕТ И ПОБЕЖДАЕТ
T1->>Map: Вызов putIfAbsent
activate Map
Map->>B: Чтение состояния ячейки tabAt
B-->>Map: null (Ячейка абсолютно пуста)
Map->>B: Аппаратная инструкция CAS(null к Context 1)
B-->>Map: Процессор: Запись подтверждена
Map-->>T1: Выход метода: null (RAM успешно засеян)
deactivate Map
Note over T1: Поток 1 продолжает работу со своим Context 1
%% ПОТОК 2 СТАРТУЕТ И ПРОИГРЫВАЕТ CAS
T2->>Map: Вызов putIfAbsent
activate Map
Map->>B: Чтение состояния ячейки tabAt
B-->>Map: null (Поток 2 видит пустоту за наносекунду до коммита Потока 1)
Map->>B: Аппаратная инструкция CAS(null к Context 2)
B-->>Map: Процессор: Отклонено (Значение памяти уже изменилось)
Map-->>T2: Выход метода: Уход на повторный круг цикла
deactivate Map
%% ПОВТОРНЫЙ ЦИКЛ ПОТОКА 2 (SPIN-LOCK RETRY)
Note over T2: Поток 2 автоматически заходит на виток 2 цикла
T2->>Map: Повторный вызов putIfAbsent
activate Map
Map->>B: Чтение состояния ячейки tabAt
B-->>Map: Состояние: Слот занят (Уже лежит Context 1)
%% ЛОКАЛЬНЫЙ LOCK БАКЕТА ПОТОКОМ 2
Note over T2, B: Точечная блокировка бакета (Блокирован только этот twin id)
Map->>B: synchronized на корень Node 1
activate B
Map->>B: Сравнение ключей через equals
B-->>Map: Идентификаторы совпали (Данные внесены Потоком 1)
Map->>B: Освобождение замка ячейки
deactivate B
Map-->>T2: Выход метода: Возврат существующего Context 1
deactivate Map
Note over T2: Поток 2 уничтожает свой дубликат Context 2 и берет в работу Context 1
Метод 6: put_if_absent_commit()
Домен: SIMULATION | Контур: Атомарный засев оперативной памяти (RAM)
В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.
- Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).
1. Бизнес-спецификация метода
- Идентификатор метода:
BPDS-SIM-M06 - Системное имя:
put_if_absent_commit() - Микросервис:
simulation-core-engine - Домен:
SIMULATION - Класс / Компонент:
simulation.engine.registry.TickRegistryImpl
1.1. Описание логики работы
Этот метод отвечает за безопасную запись скомпилированных данных пользователя в оперативную память (RAM) симулятора. Поскольку симулятор запускает сотни виртуальных потоков параллельно, может возникнуть «состояние гонки»: два независимых потока одновременно обнаружили, что пользователя нет в оперативной памяти, сходили в базу данных и одновременно прибежали записывать свои результаты.
Чтобы они не заблокировали весь симулятор и не затерли данные друг друга, метод использует специальный атомарный механизм «записи при отсутствии». Поток пытается занять свободную ячейку памяти на уровне процессора. Если он успел первым — его данные становятся эталоном. Если опоздал на наносекунду — он послушно уничтожает свой дубликат, забирает данные победителя и продолжает такт с ними.
1.2. Пошаговое выполнение
- Процессорный запрос (CAS): Поток пытается записать ссылку на собранный
TwinContextв оперативную память через быструю неблокирующую инструкцию процессора. - Фиксация победы: Если ячейка была пуста, запись подтверждается. Метод возвращает пустоту (
null), и симулятор продолжает такт с текущими данными. - Обнаружение коллизии: Если ячейка памяти уже была занята другим потоком, процессор отклоняет запись. Поток уходит на повторный автоматический круг (Spin-lock).
- Точечная блокировка: На втором круге поток вешает точечный замок (
synchronized) исключительно на ноду этого пользователя, не мешая остальным 20 000 пользователям симулятора. - Разрешение конфликта: Метод проверяет идентификаторы. Убедившись, что конкурент уже записал данные, текущий поток отбрасывает свой локальный дубликат (отдает его Сборщику мусора) и принудительно берет в работу объект, созданный победителем.
2. Диаграмма последовательности метода (Вход и Выход флоу)
Диаграмма наглядно показывает, как два параллельных потока сталкиваются на уровне ячейки памяти, как процессор аппаратно выбирает победителя, и как проигравший поток бесконфликтно переключается на эталон конкурента.
3. Схемы данных и SQL-взаимодействие
Этот метод является исключительно внутрипамятным контуром оптимизации многопоточности и напрямую с базой данных PostgreSQL не взаимодействует. Его задача — защитить RAM-реестр симулятора.
4. Спецификация обмена данными (Вход / Выход)
Данные линеаризуются внутри RAM (Heap) виртуальной машины Java.
4.1. Входной многопоточный конфликт (Входные параметры для метода)
Параллельные воркеры передают разные ссылки на сконструированные объекты:
{
"concurrency_input": {
"target_twin_id": "d3b07384-d113-4956-bf8a-e421cd7bf777",
"thread_1_allocated_address": "TwinContext@0x7f81a4b2",
"thread_2_allocated_address": "TwinContext@0x7f81c9d4"
}
}4.2. Результат линеаризации (Выходные параметры для Метода 7)
Оба потока гарантированно выходят из метода с одной общей ссылкой на эталонный объект:
{
"linearized_output": {
"retained_context_address": "TwinContext@0x7f81a4b2",
"discarded_duplicate_address": "TwinContext@0x7f81c9d4",
"resolution_status": "CONCURRENT_COLLISION_RESOLVED_ATOMICALLY"
}
}5. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: BACKEND
Заголовок: Интеграция атомарного коммита putIfAbsent в метод сохранения реестра оперативной памяти
5.1. Что нужно сделать
- Обновить внутренний метод
compileAndCommitMetricsв существующем классеTickRegistryImpl, разработанном в рамках задачиBACKEND-TASK-04. - Запись скомпилированного объекта
TwinContextв потокобезопасную карту осуществлять строго через внутренний вызовcache.putIfAbsent(twinId, newlyCompiledContext). - Реализовать логику проверки результата: если метод возвращает не
null, а ссылку на уже существующий объект, вывести предупреждающий лог уровняWARN, отказаться от локального объекта и вернуть ссылку на контекст конкурента.
6. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: МИГРАЦИЯ
Заголовок: Тюнинг параметров сборщика мусора Garbage Collector для оперативной утилизации дубликатов в Docker-контейнере
6.1. Что нужно сделать
Поскольку при массовом холодном старте сотни потоков будут мимолетно создавать тяжелые дубликаты TwinContext, которые тут же отбрасываются на шаге проверки putIfAbsent, это вызовет резкую нагрузку на область кратковременной памяти Java (Eden Space). Чтобы симулятор не уходил в микро-фризы, необходимо настроить параметры сборщика мусора G1GC в файле конфигурации деплоя docker-compose.yml.
Обновить параметры переменной JAVA_OPTS для сервиса симулятора:
environment:
- JAVA_OPTS=-Xms2000m -Xmx2000m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:G1ReservePercent=15 -XX:InitiatingHeapOccupancyPercent=45Настройка заставляет виртуальную машину Java агрессивно перемалывать отброшенные дубликаты проигравших потоков, удерживая паузы сборки мусора в пределах незаметных 20 миллисекунд.